package com.think.admin.web.service.impl;

import com.think.admin.cons.AdminCons;
import com.think.admin.model.dto.MenuTreeDTO;
import com.think.admin.model.dto.role.RoleResourceDTO;
import com.think.admin.model.dto.user.UserRoleDto;
import com.think.admin.web.service.IHomeService;
import com.think.admin.web.service.IResourceService;
import com.think.common.enums.StateEnum;
import com.think.common.exception.SysException;
import com.think.common.utils.ResultUtil;
import com.think.common.utils.SysUtils;
import com.think.dao.entity.Resource;
import com.think.dao.enums.ResourcePermissionTypeEnum;
import com.think.dao.enums.ResourceTypeEnum;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RedissonClient;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * home 服务实现类
 * </p>
 *
 * @author xwj
 * @since 2019-04-08
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = SysException.class)
public class HomeServiceImpl implements IHomeService {
    private final RedissonClient redissonClient;
    private final IResourceService resourceService;

    @Override
    public HttpEntity menu(String userId) {
        List<MenuTreeDTO> menuTrees;
        if (AdminCons.Param.SUPER_ADMIN_ID.equals(userId)) {
            // 管理员：从数据库中获取全部资源信息
            menuTrees = resourceService.lambdaQuery()
                    .in(Resource::getType, ResourceTypeEnum.DIRECTORY, ResourceTypeEnum.MENU)
                    .eq(Resource::getState, StateEnum.ENABLE).list().stream()
                    .map(e -> e.convert(MenuTreeDTO.class))
                    .collect(Collectors.toList());
            if (SysUtils.isEmpty(menuTrees)) {
                return ResultUtil.success(Collections.emptyList());
            }
        } else {
            // 非管理员，从缓存获取用户角色资源
            Map<String, UserRoleDto> userRoleDtoMap = redissonClient.getMap(AdminCons.Redis.USER_ROLE + userId);
            if (SysUtils.isNotEmpty(userRoleDtoMap)) {
                menuTrees = userRoleDtoMap.keySet().stream()
                        .map(e -> redissonClient.<String, RoleResourceDTO>getMap(AdminCons.Redis.ROLE_RESOURCE + e).values())
                        .flatMap(Collection::stream)
                        .map(e -> e.convert(MenuTreeDTO.class))
                        .distinct()
                        .collect(Collectors.toList());
            }else{
                menuTrees = new ArrayList<>();
            }

            // 非管理员，从数据库查出只需登录的目录和菜单
            menuTrees.addAll(resourceService.lambdaQuery()
                    .eq(Resource::getState, StateEnum.ENABLE)
                    .eq(Resource::getPermissionType, ResourcePermissionTypeEnum.LOGIN)
                    .in(Resource::getType, ResourceTypeEnum.DIRECTORY, ResourceTypeEnum.MENU)
                    .list().stream()
                    .map(e -> e.convert(MenuTreeDTO.class)).collect(Collectors.toList()));
            if (SysUtils.isEmpty(menuTrees)) {
                return ResultUtil.success(Collections.emptyList());
            }
        }

        // 过滤出为目录和菜单对象
        List<MenuTreeDTO> dirMenu = menuTrees.stream()
                .filter(this::isDirOrMenu)
                .collect(Collectors.toList());

        // 构造菜单树返回
        return ResultUtil.success(dirMenu.stream()
                .filter(this::isDirectory)
                .sorted(Comparator.comparing(MenuTreeDTO::getSort))
                .map(e -> findChildren(e, dirMenu))
                .peek(e -> e.getChildrenList().sort(Comparator.comparing(MenuTreeDTO::getSort)))
                .collect(Collectors.toList()));
    }

    /**
     * 判断是否为目录
     *
     * @param obj
     * @return
     */
    private boolean isDirectory(MenuTreeDTO obj) {
        return SysUtils.isNotEmpty(obj) && "0".equals(obj.getParentId()) && ResourceTypeEnum.DIRECTORY.equals(obj.getType());
    }

    /**
     * 判断是否为目录或者菜单
     *
     * @param obj
     * @return
     */
    private boolean isDirOrMenu(MenuTreeDTO obj) {
        return SysUtils.isNotEmpty(obj) && (ResourceTypeEnum.DIRECTORY.equals(obj.getType())
                || ResourceTypeEnum.MENU.equals(obj.getType()));
    }

    /**
     * 递归查找子节点
     *
     * @param treeNodes
     * @return
     */
    private MenuTreeDTO findChildren(MenuTreeDTO treeNode, List<MenuTreeDTO> treeNodes) {
        treeNodes.stream().filter(e -> e.getParentId().equals(treeNode.getId()))
                .forEach(e -> treeNode.getChildrenList().add(e));
        return treeNode;
    }
}
